﻿using System.Windows.Forms;
using SharpDX;
using ProtoBuf;

namespace Engine
{
    /// <summary>
    /// Класс камеры
    /// </summary>
    public class EEShadowCamera : EEDCamera, System.IDisposable
    {

        #region IDisposable
        ~EEShadowCamera()
        {
            Dispose();
        }
        public void Dispose()
        {

        }
        #endregion

        new public Vector3 position;

        public EEShadowCamera() { }

        /// <summary>
        /// Конструктор теневой камеры
        /// </summary>
        /// <param name="Size">Размер</param>
        /// <param name="Fov">Угол обзора</param>
        /// <param name="NearClip">Ближняя плоскость отсечения</param>
        /// <param name="FarClip">Дальняя плоскость отсечения</param>
        public EEShadowCamera(float NearClip, float FarClip)
        {
            aspectRatio = 1.0f;
            fov = 1f;
            nearClip = NearClip;
            farClip = FarClip;
          }

        /// <summary>
        /// Обновление камеры
        /// </summary>
        public void Update(Vector3 CamPos, Vector3 Target)
        {
            Quaternion quaternion = Utils.QuaternionFromTwoDirs(new Vector3(0, 0, 1), Target - CamPos);
            position = CamPos;

            Vector3 tmp_vec = new Vector3(0, 1, 0);//Up Vector
            up_vector = Vector3.Transform(tmp_vec, quaternion);
            up_vector = tmp_vec;
            tmp_vec = new Vector3(0, 0, 1);//Front Vector
            direction = Vector3.Transform(tmp_vec, quaternion);

            viewMatrix = Matrix.LookAtLH(position, position + direction, up_vector);

            if (fov < 0.01f) fov = 0.01f;
            if (fov > 3f) fov = 3f;

            projectionMatrix = Matrix.PerspectiveFovLH(fov, aspectRatio, nearClip, farClip);

            viewProjectionMatrix = viewMatrix * projectionMatrix;

            cameraFrustum = new BoundingFrustum(viewProjectionMatrix);

            invertViewMatrix = Matrix.Invert(viewMatrix);

            yFac = (float)System.Math.Tan(fov / 2f);
            xFac = yFac * aspectRatio;
        }

        
        public float Split12, Split23;

        public void SetSplit(Vector3[] Fr0, Vector3 PosN,float FovCor)
        {
            Vector3 tt_ = (Fr0[0] + Fr0[1] + Fr0[2] + Fr0[3] + Fr0[4] + Fr0[5] + Fr0[6] + Fr0[7]) / 8f;
            Update(PosN, tt_);
            //return;

            float maxX = 0, maxY = 0, minX = 0, minY = 0, maxZ = 0, minZ = 0;
            bool one = true;
            for (int t = 0; t < 8; t++ )
                {
                    Vector3 Temp = Fr0[t];
                    Vector3 T = Conversion.ToVector3(Vector3.Transform(Temp, viewMatrix));
                    float Ax = (float)System.Math.Asin(T.X / (new Vector3(T.X, 0f, T.Z)).Length());
                    float Ay = (float)System.Math.Asin(T.Y / (new Vector3(0f, T.Y, T.Z)).Length());
                    float Dz = (PosN - Temp).Length();
                    if (one)
                    {
                        minX = Ax;
                        maxX = Ax;
                        minY = Ay;
                        maxY = Ay;
                        minZ = Dz;
                        maxZ = Dz;
                        one = false;
                    }
                    if (Ax < minX) minX = Ax;
                    if (Ax > maxX) maxX = Ax;
                    if (Ay < minY) minY = Ay;
                    if (Ay > maxY) maxY = Ay;
                    if (Dz < minZ) minZ = Dz;
                    if (Dz > maxZ) maxZ = Dz;
                }

            float D_ = maxZ;// tt_.Length();
            float tminX = (float)System.Math.Sin(minX) * D_;
            float tminY = (float)System.Math.Sin(minY) * D_;
            float tmaxX = (float)System.Math.Sin(maxX) * D_;
            float tmaxY = (float)System.Math.Sin(maxY) * D_;
            //кусок кода для тестов
//             ulong DD = 0, D = 0;
//             unsafe
//             {
//                 SSEmath.SSEmath.CpuTicks((uint)&DD);
//             }
           
            Vector3 t00 = new Vector3(tminX, tminY, D_);
            Vector3 t02 = new Vector3(tmaxX, tmaxY, D_);
            Vector3 t01 = new Vector3(tminX, tmaxY, D_);
            Vector3 t03 = new Vector3(tmaxX, tminY, D_);
            unsafe
            {
                Matrix invertViewMatrix = this.invertViewMatrix;
                
                t00 = Conversion.ToVector3(Vector3.Transform(t00, invertViewMatrix));
                t01 = Conversion.ToVector3(Vector3.Transform(t01, invertViewMatrix));
                t02 = Conversion.ToVector3(Vector3.Transform(t02, invertViewMatrix));
                t03 = Conversion.ToVector3(Vector3.Transform(t03, invertViewMatrix));
                
                //SSEmath.SSEmath.Transform_void_Vector3_Vector3_Vector3_Vector3_Matrix4x4((uint)&t00, (uint)&t01, (uint)&t02, (uint)&t03, (uint)&invertViewMatrix);
            }
            //кусок кода для тестов
//             unsafe
//             {
//                 SSEmath.SSEmath.CpuTicks((uint)&D);
//             }
//             D -= DD;
//             SSEmath.SSEmath.counter++;
//             if (SSEmath.SSEmath.min > D)
//             {
//                 SSEmath.SSEmath.min = D;
//             }
//             if (SSEmath.SSEmath.counter == 5)
//             {
//                 SSEmath.SSEmath.counter = 0;
//                 Console.WriteLine("min: " + SSEmath.SSEmath.min + "\treal: " + D);
//                 SSEmath.SSEmath.min = 999999999;
//             }
            //
            fov = (maxY - minY);// *FovCor;
            aspectRatio = (maxX - minX) / fov;
            
            Vector3 Cr = (t00 + t01 + t02 + t03) / 4f;
            farClip = maxZ * 2f;
            nearClip = minZ / 2f;

            Update(PosN, Cr);
        }
    }
}
